/*
 * This software is published under the Apchae 2.0 licenses.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * Author: Erik Scholtz 
 * Web: http://blog.elitecoderz.net
 */

package net.elitecoderz.blog;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * AutoMounter - Check if a network volume (afp or smb) is mounted or not
 * if the volume is already mounted, nothing will be done at all
 * if the volume is not mounted, the class tries to mount it
 * @author scholtz
 */
public class VolumeMounter {
	private final static String VOLUMESROOT_OSX = "/Volumes/";

	/**
	 * Method to mount a given URL to a server volume, provided via afp or smb
	 * The URL is considered to be in the following format:
	 * 
	 * 			<protocol>://<username>:<password>@<server-address>/<path>/
	 * 			example:
	 * 			afp://firstname, lastname:mypassword@file.serv.er/Volumename/
	 * 			smb://theuser:hispassword@another.serv.er/sharename/
	 * @param path
	 * @return
	 * @throws Exception
	 */
	public Boolean mount(String path) throws Exception {
		String vers = System.getProperty("os.name").toLowerCase();

		if (path.startsWith("afp://") || path.startsWith("smb://")) {
			if (vers.indexOf("windows") != -1) {            // WINDOWS
				throw new Exception("Windows is not yet supported by the VolumeMounter Class");
			} else if (vers.indexOf("mac") != -1) {			// OS X
				// Create the mountpoint under the VOLUMES_OSX path
				Integer pos = path.indexOf("/", 7);
				String serverPath = path.substring(pos);
				String[] folders = serverPath.split("/");
				String mountPoint = null;
				if (folders.length <= 0) {
					mountPoint = createOSXMountPoint("servermount");
				} else {
					mountPoint = createOSXMountPoint(folders[folders.length-1]);
				}
				// Mount the volume under the new mountpoint
				Process p = null;
				if (path.startsWith("afp://")) {			// AFP volumes
					p = Runtime.getRuntime().exec(new String[] {"/sbin/mount_afp","-i",path,mountPoint});
					p.waitFor();
				} else if (path.startsWith("smb://")) {		// SMB volumes
					String newPath = path.substring(4);
					p = Runtime.getRuntime().exec(new String[] {"/sbin/mount","-t","smbfs",newPath,mountPoint});
					p.waitFor();
				} else {
					// We never should get here - just to ensure the volume wasn't mounted if this line is hit
					return false;
				}
				checkResultOSX(p.getErrorStream(),mountPoint);
			}
		} else {
			throw new Exception("Unknown mount-type: the path must either start with \"afp://\" or \"smb://\"");
		}
		return true;
	}
	
	/**
	 * For OSX:
	 * Check the error-message of the mount-process. Remove the new created mountpoint if the volume
	 * was already mounted, to clean up the /Volumes dictionary
	 * @param is
	 * @param mountPoint
	 * @throws Exception
	 */
	private void checkResultOSX(InputStream is, String mountPoint) throws Exception {
		String errMsg = inputStreamToString(is);
		if (errMsg.length()>0) {
			if (errMsg.contains("the volume is already mounted")) {
				//AFP could not be mounted
				//expected message: mount_afp: the volume is already mounted
				removeMountPointOSX(mountPoint);
			} else if (errMsg.contains("mount error") && errMsg.contains("File exists")) {
				//SMB could not be mounted
				//expected message: mount_smbfs: mount error: /Volumes/<yourmountpoint>: File exists
				removeMountPointOSX(mountPoint);
			} else {
				throw new Exception("Unknown error message: "+errMsg);
			}
		}
	}
	
	/**
	 * For OSX:
	 * Remove the mountpoint if it is empty. Throws a SecurityException if the folder isn't empty
	 * @param path
	 */
	private void removeMountPointOSX(String path) throws Exception {
		File myDir = new File(path);
		myDir.delete();
	}

	/**
	 * For OSX:
	 * Create a new Mountpoint - if the desired name already exists, we'll add a number
	 * till we find an unused mointpoint-name
	 * @param foldername
	 * @return
	 */
	private String createOSXMountPoint(String foldername) {
		File newFile = new File(VOLUMESROOT_OSX+foldername);
		Integer i = 0;
		while(newFile.exists()) {
			newFile = new File(VOLUMESROOT_OSX+foldername+i.toString());
			i++;
		}
		newFile.mkdir();
		return (newFile.getAbsolutePath());
	}
	
	/**
	 * Simple method that converts an InputStream into a String
	 * @param in
	 * @return
	 * @throws IOException
	 */
	private String inputStreamToString(InputStream in) throws IOException {
		BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
		StringBuilder stringBuilder = new StringBuilder();
		String line = null;

		while ((line = bufferedReader.readLine()) != null) {
			stringBuilder.append(line + "\n");
		}

		bufferedReader.close();
		return stringBuilder.toString();
	}

}
